//
//  KLTextView.m
//  KLTextView
//
//  Created by PPAbner on 2020/9/3.
//

/**
 ## 已知问题
 > 2024.01.05
 ### `Xcode` 控制台警告:
 > UITextView 4,404,171,776 is switching to TextKit 1 compatibility mode because its layoutManager was accessed. Break on void _UITextViewEnablingCompatibilityMode(UITextView *__strong, BOOL) to debug.
 
 #### 参考链接
 - [iOS UITextView 渲染富文本-性能优化](https://blog.csdn.net/lyh1083908486/article/details/134009879) `2023.10.24`
 
 ⚠️：根据参考链接的方案，并没有解决！
 */

#import "KLTextView.h"

#pragma mark --- view

///top
CG_INLINE void setTopOf(UIView *v, CGFloat top){
    CGRect frame = v.frame;
    frame.origin.y = top;
    v.frame = frame;
}

/////底
//CG_INLINE CGFloat bottomOf(UIView *v){
//    return v.frame.origin.y + v.frame.size.height;
//}
//
CG_INLINE void setBottomOf(UIView *v, CGFloat bottom){
    CGRect frame = v.frame;
    frame.origin.y = bottom - frame.size.height;
    v.frame = frame;
}
//
///left
//CG_INLINE CGFloat leftOf(UIView *v){
//    return v.frame.origin.x;
//}

CG_INLINE void setLeftOf(UIView *v, CGFloat left){
    CGRect frame = v.frame;
    frame.origin.x = left;
    v.frame = frame;
}

///right
CG_INLINE void setRightOf(UIView *v, CGFloat right){
    CGRect frame = v.frame;
    frame.origin.x = right - frame.size.width;
    v.frame = frame;
}


///宽
CG_INLINE CGFloat widthOf(UIView *v){
    return v.frame.size.width;
}

CG_INLINE void setWidthOf(UIView *v, CGFloat width){
    CGRect frame = v.frame;
    frame.size.width = width;
    v.frame = frame;
}

///高
CG_INLINE CGFloat heightOf(UIView *v){
    return v.frame.size.height;
}

CG_INLINE void setHeightOf(UIView *v, CGFloat height){
    CGRect frame = v.frame;
    frame.size.height = height;
    v.frame = frame;
}

///centerY
CG_INLINE CGFloat centerYOf(UIView *v){
    return v.center.y;
}

CG_INLINE void setCenterYOf(UIView *v, CGFloat centerY){
    CGPoint c = v.center;
    c.y = centerY;
    v.center = c;
}

UIKIT_STATIC_INLINE UIColor *colorWithRGBA(CGFloat r, CGFloat g, CGFloat b, CGFloat a){
    return [UIColor colorWithRed:r / 255.0 green:g / 255.0 blue:b / 255.0 alpha:a];
};

typedef NS_ENUM(NSInteger, KLTextVerticalAlignment) {
    KLTextVerticalAlignmentTop = 0,
    KLTextVerticalAlignmentMiddle,
    KLTextVerticalAlignmentBottom
};

@interface KLTextViewInternalLabel : UILabel
@property (nonatomic, assign) KLTextVerticalAlignment verticalAlignment;
@end


@interface KLTextView ()<UITextViewDelegate>
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UITextView *textView;
@property (nonatomic, strong) KLTextViewInternalLabel * placeholderLabel;
@property (nonatomic, assign) KLTextViewStyle style;
@property (nonatomic, assign) CGFloat lastHeight;
@property (nonatomic, strong) UILabel *textCountLabel;
@property (nonatomic, assign) NSInteger maxTextCount;
@property (nonatomic, assign) BOOL isSingleLine;
@end

@implementation KLTextView


#pragma mark --- life cycle
- (instancetype)initWithFrame:(CGRect)frame style:(KLTextViewStyle)style
{
    self = [super initWithFrame:frame];
    if (self) {
        self.style = style;
        [self setupDefaultValues];
        [self createUI];
        [self addTap];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
    return [self initWithFrame:CGRectZero style:(KLTextViewStyleMaxMinHeight)];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    if (_isSingleLine) {
        setHeightOf(self.contentView, heightOf(self.textView) + self.textContainerInset.top + self.textContainerInset.bottom);
    }else{
        setHeightOf(self.contentView, heightOf(self.textView) + self.textContainerInset.top + self.textContainerInset.bottom * 2 + heightOf(self.textCountLabel));
    }
    setHeightOf(self.placeholderLabel, heightOf(self.textView));
    
}

#pragma mark --- public method
- (void)setCorcerRadius:(CGFloat)cornerRadius
{
    self.layer.masksToBounds = YES;
    self.layer.cornerRadius = cornerRadius;
}

- (void)configureMaxTextCount:(NSInteger)maxTextCount font:(nullable UIFont *)font textColor:(nullable UIColor *)textColor isSingleLine:(BOOL)isSingleLine
{
    if (maxTextCount <= 0) {
        return;
    }
    
    _maxTextCount = maxTextCount;
    _isSingleLine = isSingleLine;
    [self addSubview:self.textCountLabel];
    
    if (font) {
        self.textCountLabel.font = font;
    }
    if (textColor) {
        self.textCountLabel.textColor = textColor;
    }
    
    self.textCountLabel.text = [NSString stringWithFormat:@"0/%ld",(long)maxTextCount];
    [self updateUI];
}

#pragma mark --- private mothod
- (void)setupDefaultValues
{
    if (self.style == KLTextViewStyleMaxMinHeight) {
        self.maximumHeight = 100;
        self.minimumHeight = 45;
    }else{
        self.maximumLine = 5;
        self.minimumLine = 1;
    }
    self.textContainerInset = UIEdgeInsetsMake(5, 10, 5, 10);
    self.textColor = colorWithRGBA(0, 0, 0, 1.0);
    self.font = [UIFont systemFontOfSize:12];
}

- (void)createUI
{
    self.backgroundColor = colorWithRGBA(255, 255, 255, 1.0);
    
    [self addSubview:self.contentView];
    setLeftOf(self.contentView, 0);
    setWidthOf(self.contentView, widthOf(self));
    
    [self.contentView addSubview:self.textView];
    [self.contentView addSubview:self.placeholderLabel];

    [self resetTextViewAndPlaceholderLabelFrame];
}

- (void)resetTextViewAndPlaceholderLabelFrame
{
    setTopOf(self.textView, self.textContainerInset.top);
    setLeftOf(self.textView, self.textContainerInset.left);
    setWidthOf(self.textView, widthOf(self.contentView) - self.textContainerInset.left - self.textContainerInset.right);
    
    setTopOf(self.placeholderLabel, self.textContainerInset.top);
    setLeftOf(self.placeholderLabel, self.textContainerInset.left);
    setWidthOf(self.placeholderLabel, widthOf(self.contentView) - self.textContainerInset.left - self.textContainerInset.right);
}

- (void)addTap
{
    UITapGestureRecognizer *tapG = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedSelf)];
    [self addGestureRecognizer:tapG];
}

- (void)tappedSelf
{
    [self.textView becomeFirstResponder];
}

- (void)didMoveToWindow
{
    [super didMoveToWindow];
    [self updateUI];
}


- (void)updateUI
{
    
    [self updatePlaceholderLabel];
    [self updateUI_maxMinLine];
    [self updateUI_maxMinHeight];
    
    if (_maxTextCount > 0) {
        [self.textCountLabel sizeToFit];
        if (_isSingleLine) {
            setCenterYOf(self.textCountLabel, centerYOf(self.textView));
        }else{
            setHeightOf(self, heightOf(self) + heightOf(self.textCountLabel) + self.textContainerInset.bottom);
            setBottomOf(self.textCountLabel, heightOf(self) - self.textContainerInset.bottom);
        }
        setRightOf(self.textCountLabel, widthOf(self) - self.textContainerInset.right - 2);///-2处理指示器宽

    }

    CGFloat height = heightOf(self);
    if (self.lastHeight != height) {
        if (self.didChangedHeightBlcok) {
            self.didChangedHeightBlcok(self, height);
        }
        self.lastHeight = height;
    }
    
}

- (void)updateUI_maxMinHeight
{
    if (self.style != KLTextViewStyleMaxMinHeight) {
        return;
    }
    
    CGFloat textViewH = self.textView.contentSize.height;
    CGFloat maxTextViewH = self.maximumHeight - self.textContainerInset.top - self.textContainerInset.bottom;
    CGFloat minTextViewH = self.minimumHeight - self.textContainerInset.top - self.textContainerInset.bottom;
    ///设置最大值
    if (textViewH <= maxTextViewH) {
        setHeightOf(self.textView, textViewH);
    }else{
        setHeightOf(self.textView, maxTextViewH);
    }

    ///设置最小值
    if (heightOf(self.textView) <= minTextViewH) {
        setHeightOf(self.textView, minTextViewH);
    }
    
    setHeightOf(self, heightOf(self.textView) + self.textContainerInset.top + self.textContainerInset.bottom);
}

- (void)updateUI_maxMinLine
{
    if (self.style != KLTextViewStyleMaxMinLine) {
        return;
    }
    
    CGFloat textViewH = self.textView.contentSize.height;
    CGFloat oneLineH = self.textView.font.lineHeight;
    CGFloat maxTextViewH = ceil(oneLineH * self.maximumLine);
    CGFloat minTextViewH = ceil(oneLineH * self.minimumLine);
    ///设置最大值
    if (textViewH <= maxTextViewH) {
        setHeightOf(self.textView, textViewH);
    }else{
        setHeightOf(self.textView, maxTextViewH);
    }

    ///设置最小值
    if (heightOf(self.textView) <= minTextViewH) {
        setHeightOf(self.textView, minTextViewH);
    }

    setHeightOf(self, heightOf(self.textView) + self.textContainerInset.top + self.textContainerInset.bottom);

}

- (void)updateTextCountLabel
{
    [self.textCountLabel sizeToFit];
    
    if (_isSingleLine) {
        setCenterYOf(self.textCountLabel, centerYOf(self.textView));
    }else{
        setHeightOf(self, heightOf(self) + heightOf(self.textCountLabel) + self.textContainerInset.bottom);
        setBottomOf(self.textCountLabel, heightOf(self) - self.textContainerInset.bottom);
    }
    setRightOf(self.textCountLabel, widthOf(self) - self.textContainerInset.right - 2);///-2处理指示器宽
}

- (void)updatePlaceholderLabel
{
    if (self.textView.text.length > 0 || self.textView.attributedText.string.length > 0) {
        self.placeholderLabel.hidden = YES;
    }else{
        self.placeholderLabel.hidden = NO;
    }
}


#pragma mark --- UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView {
    if (_maxTextCount > 0 && textView.text.length > _maxTextCount) {
        textView.text = [textView.text substringToIndex:_maxTextCount];
    }
    
    self.textCountLabel.text = [NSString stringWithFormat:@"%lu/%ld",(unsigned long)self.textView.text.length,(long)_maxTextCount];

    if (self.didChangedTextBlcok) {
        self.didChangedTextBlcok(self);
    }
    [self updateUI];
}

#pragma mark --- setter
- (void)setText:(NSString *)text
{
    self.textView.text = text;
    if (_maxTextCount > 0) {
        self.textCountLabel.text = [NSString stringWithFormat:@"%lu/%ld",(unsigned long)text.length,(long)_maxTextCount];
    }
    [self updateUI];
}

- (NSString *)text
{
    return self.textView.text;
}

- (void)setTextColor:(UIColor *)textColor
{
    if (!textColor) {
        textColor = colorWithRGBA(0, 0, 0, 1.0);
    }
    _textColor = textColor;
    self.textView.textColor = textColor;
}

- (void)setAttributedText:(NSAttributedString *)attributedText
{
    _attributedText = attributedText;
    self.textView.attributedText = attributedText;
    [self updateUI];
}

- (void)setFont:(UIFont *)font
{
    if (!font) {
        font = [UIFont systemFontOfSize:12];
    }
    _font = font;
    self.textView.font = font;
    self.placeholderLabel.font = font;
    [self updateUI];
}

- (void)setPlaceholder:(NSString *)placeholder
{
    _placeholder = placeholder;
    self.placeholderLabel.text = placeholder;
}

- (void)setPlaceholcerColor:(UIColor *)placeholcerColor
{
    _placeholcerColor = placeholcerColor;
    if (!_placeholcerColor) {
        _placeholcerColor = colorWithRGBA(128, 128, 128, 0.7);
    }
    self.placeholderLabel.textColor = placeholcerColor;
}

- (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder
{
    _attributedPlaceholder = attributedPlaceholder;
    self.placeholderLabel.attributedText = attributedPlaceholder;
    [self updateUI];
}

- (void)setMaximumLine:(CGFloat)maximumLine
{
    _maximumLine = maximumLine;
    if (!_maximumLine || _maximumLine <= 0) {
        _maximumLine = 5;
    }
    if (_minimumLine > _maximumLine) {
        _maximumLine = _minimumLine;
    }
}

- (void)setMinimumLine:(CGFloat)minimumLine
{
    _minimumLine = minimumLine;
    if (!_minimumLine || _minimumLine <= 0) {
        _minimumLine = 1;
    }
    if (_minimumLine > _maximumLine) {
        _maximumLine = _minimumLine;
    }
}

- (void)setMaximumHeight:(CGFloat)maximumHeight
{
    _maximumHeight = maximumHeight;
    if (_maximumHeight <= 0) {
        _maximumHeight = 100;
    }
    
    if (_minimumHeight > _maximumHeight) {
        _maximumHeight = _minimumHeight;
    }
}

- (void)setMinimumHeight:(CGFloat)minimumHeight
{
    _minimumHeight = minimumHeight;
    if (_minimumHeight <= 0) {
        _minimumHeight = 45;
    }
    if (_minimumHeight > _maximumHeight) {
        _maximumHeight = _minimumHeight;
    }
}

- (void)setBackgroundColor:(UIColor *)backgroundColor
{
    self.contentView.backgroundColor = backgroundColor;
    self.textView.backgroundColor = backgroundColor;
}

- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset
{
    _textContainerInset = textContainerInset;
    [self resetTextViewAndPlaceholderLabelFrame];
}

#pragma mark --- lazy load
- (UIView *)contentView
{
    if (!_contentView) {
        _contentView = [[UIView alloc] init];
    }
    return _contentView;
}

- (UITextView *)textView
{
    if (!_textView) {
        _textView = [[UITextView alloc] init];
        _textView.delegate = self;
        _textView.enablesReturnKeyAutomatically = YES;
        _textView.layoutManager.allowsNonContiguousLayout = NO;
        _textView.scrollsToTop = NO;
        _textView.textContainerInset = UIEdgeInsetsZero;
        _textView.textContainer.lineFragmentPadding = 0;
    }
    return _textView;;
}

- (UILabel *)placeholderLabel
{
    if (!_placeholderLabel) {
        _placeholderLabel = [[KLTextViewInternalLabel alloc] init];
        _placeholderLabel.textColor = colorWithRGBA(128, 128, 128, 0.7);
        _placeholderLabel.numberOfLines = 0;
    }
    return _placeholderLabel;
}

- (UILabel *)textCountLabel
{
    if (!_textCountLabel) {
        _textCountLabel = [[UILabel alloc] init];
        _textCountLabel.textAlignment = NSTextAlignmentRight;
        _textCountLabel.font = [UIFont systemFontOfSize:13];
        _textCountLabel.textColor = colorWithRGBA(153, 153, 153, 1.0);///999999
    }
    return _textCountLabel;
}

@end


@implementation KLTextViewInternalLabel

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.verticalAlignment = KLTextVerticalAlignmentTop;
    }
    return self;
}
 
- (void)setVerticalAlignment:(KLTextVerticalAlignment)verticalAlignment {
    _verticalAlignment = verticalAlignment;
    [self setNeedsDisplay];
}
 
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
    CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
    textRect.origin.x = 3;
    switch (self.verticalAlignment) {
        case KLTextVerticalAlignmentTop:
            textRect.origin.y = bounds.origin.y;
            break;
        case KLTextVerticalAlignmentBottom:
            textRect.origin.y = bounds.origin.y + bounds.size.height - textRect.size.height;
            break;
        case KLTextVerticalAlignmentMiddle:
            // Fall through.
        default:
            textRect.origin.y = bounds.origin.y + (bounds.size.height - textRect.size.height) / 2.0;
    }
    return textRect;
}
 
- (void)drawTextInRect:(CGRect)requestedRect {
    CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
    [super drawTextInRect:actualRect];
}

@end
